/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/* Copyright 2002-2003 by Mark Wolgemuth, All rights reserved.             */
/* Copyright 2002-2003 by Employease, Inc.  All rights reserved.           */
/*                                                                         */
/* Redistribution and use in source and binary forms, with or without      */
/* modification, are permitted provided that the following conditions      */
/* are met:                                                                */
/*                                                                         */
/*    1. Redistributions of source code must retain the above copyright    */
/* notice, this list of conditions and the following disclaimer.           */
/*    2. Redistributions in binary form must reproduce the above copyright */
/* notice, this list of conditions and the following disclaimer in the     */
/* documentation and/or other materials provided with the distribution.    */
/*                                                                         */
/* THIS SOFTWARE IS PROVIDED BY THE MOD_ATHENA PROJECT ``AS IS'' AND ANY   */
/* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE       */
/* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR      */
/* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FREEBSD PROJECT OR        */
/* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,   */
/* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,     */
/* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR      */
/* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY     */
/* OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT            */
/* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE   */
/* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.    */
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */


#include "ath_util.h"
#include "athd_util_types.h"

athd_directive * 
athd_directive_alloc(apr_pool_t *p)
{
    athd_directive *direc = (athd_directive *)
	apr_pcalloc(p, sizeof(athd_directive));
    direc->parent = NULL;
    direc->children = ath_list_ap_alloc(p);
    direc->cmd = NULL;
    direc->args = ath_list_ap_alloc(p);
    return direc;
}

athd_directive * 
athd_directive_add_child(apr_pool_t *p, 
        athd_directive *d_parent, char *cmd, char *arg, int linenum)
{
    athd_directive *direc = athd_directive_alloc(p);
    ASSERT(d_parent);
    ASSERT(cmd);
    direc->parent = d_parent;
    direc->cmd = cmd;
    direc->linenum = linenum;
    ath_list_ap_add(p, d_parent->children, direc);
    if (arg)
	ath_list_ap_add(p, direc->args, arg);
    return direc;
}

const char * 
athd_directive_gen_tree(apr_pool_t *pool, 
	athd_directive **dtree, const char *path)
{
    athd_directive *dcur;
    apr_file_t *file;
    char *buf;
    char *str_cmd, *str_arg;
    int rv;
    int linenum = 0;
    athd_nest_type nest = ATHD_NEST_NONE;
    char *sep = " \t,";
    char *line, *str_tok, *inter;
    const char *str_err = NULL;
    ASSERT(dtree && path);

    rv = apr_file_open(&file, path, APR_READ, APR_OS_DEFAULT, pool);
    if (rv)
    {
	str_err = athd_err_str(pool, rv);
	str_err = apr_pstrcat(pool, str_err, ": ", path, NULL);
    }

    /* create directive tree root node */
    /* root node is distinguished by its NULL cmd */
    (*dtree) = athd_directive_alloc(pool);
    dcur = *dtree;
    
    while ((str_err == NULL) 
	    && ((line = ath_getline(pool, file, ATHD_MAX_GETLINE))
		!= NULL))
    {
	linenum++;
	str_cmd = NULL;
	str_arg = NULL;
	ath_trim_comment(line);
	ath_chomp_ws(line);

	/* ATHD_PRINTOUT("%d) %s\n", linenum, line); */

	if ((*line == '<') && (*(line + 1) != '/'))
	{
	    line++;
	    nest = ATHD_NEST_OPEN;
	}
	else if ((*line == '<') && (*(line + 1) == '/'))
	{
	    line = line + 2;
	    nest = ATHD_NEST_CLOSE;
	}

	if ((nest == ATHD_NEST_OPEN)
		|| (nest == ATHD_NEST_CLOSE))
	{
	    if (*(line + strlen(line) - 1) == '>')
		*(line + strlen(line) - 1) = (int)NULL;
	    else
	    {
		str_err = apr_psprintf(pool, ATHD_ERR_TAGFMT, line);
		break; /* sad, but true */
	    }
	}

	str_cmd = apr_strtok(line, sep, &inter);

	if (nest == ATHD_NEST_CLOSE)
	{
	    dcur->linenum = linenum;
	    /* is it safe close? */
	    if (ath_streq(str_cmd, dcur->cmd))
	      {
		/* go up node */
		dcur = dcur->parent;
		nest = ATHD_NEST_NONE;
	      }
	    else
		str_err = apr_psprintf(pool, ATHD_ERR_TAGOPN, 
			dcur->cmd);
	}
	else
	{
	    if (str_cmd)
	    {
		athd_directive *dtmp; /* add args to appropriate list */
		char *str_argcpy;
		char *str_cmdcpy = apr_pstrdup(pool, str_cmd);
		

		if (nest == ATHD_NEST_OPEN)
		    /* go down node */
		    /* moves current direc to new node */
		  {
		      dcur = athd_directive_add_child(pool, dcur, 
						      str_cmdcpy, NULL, 
						      linenum);
		      dtmp = dcur;
		      nest = ATHD_NEST_IN;
		  }
		else /* either ATHD_NEST_IN or top */
		  {
		      dtmp = athd_directive_add_child(pool, dcur,
						      str_cmdcpy, NULL,
						      linenum);
		      
		  }
		/* permits no arg, adds extras */
		while ((str_arg = apr_strtok(NULL, sep, &inter))
		       != NULL)
		  {
		      str_argcpy = apr_pstrdup(pool, str_arg);
		      ath_list_ap_add(pool, dtmp->args, str_argcpy);
		  }
	    }
	}
    }
    return str_err;
}

int 
athd_directive_traverse(athd_directive **dstate, athd_tree_state *tstate)
{
    int rv;
    athd_directive *dnew = NULL;
    ASSERT(dstate && tstate);

    if ((*tstate) == ATHD_TREE_VIS)
      {
	  /* has any children */
	  if (!(ath_list_isempty((*dstate)->children)))
	    {
		ath_list_rewind((*dstate)->children);
		/* get first child */
		dnew = (athd_directive *)
		  ath_list_getnext((*dstate)->children);
		/* new position, not expanded yet, return current */
		(*dstate) = dnew;
		rv = 1;
	    }
	  else
	    {
		/* this path expanded, recurse up */
		(*tstate) = ATHD_TREE_EXP;
		dnew = (*dstate)->parent;
		if (dnew)
		  {
		      (*dstate) = dnew;
		      athd_directive_traverse(dstate, tstate);
		  }
		else
		  {
		      (*dstate) = NULL;
		      rv = 0;
		  }
	    }
      }
    else
      {
	  /* cannot be empty, we were already here */
	  if (ath_list_hasnext((*dstate)->children))
	    {
		/* get next child */
		dnew = (athd_directive *)
		  ath_list_getnext((*dstate)->children);
		/* visited not expanded */
		(*dstate) = dnew;
		(*tstate) = ATHD_TREE_VIS;
		rv = 1;
	    }
	  else
	    {
		/* recurse up until we get another child */
		dnew = (*dstate)->parent;
		if (dnew)
		  {
		      (*dstate) = dnew;
		      athd_directive_traverse(dstate, tstate);
		  }
		else
		  {
		      (*dstate) = NULL;
		      rv = 0;
		  }
	    }
      }
    return rv;
}

char *
athd_directive_tree_to_str(apr_pool_t *pool, athd_directive *dtree)
{
    athd_directive *dcur;
    athd_tree_state tstate = ATHD_TREE_VIS;
    char *str_out = "TOP {\n";

    /* start at top, 1st iter */
    dcur = dtree;
    athd_directive_traverse(&dcur, &tstate);

    while (dcur)
      {
	  char *arg = NULL;
	  char *pararg = NULL;
	  str_out = apr_pstrcat(pool, str_out,
				"\ncmd: ", dcur->cmd,
				(apr_psprintf(pool, "(%d)", dcur->linenum)),
				"\n  cmd parent: ", dcur->parent->cmd, NULL);
	  ath_list_rewind(dcur->args);
	  /* really, parent only has one arg (its name) (i hope) */
	  if (! ath_list_isempty(dcur->parent->args))
	    {
		ath_list_rewind(dcur->parent->args);
		pararg = 
		  (char *) ath_list_getnext(dcur->parent->args);
	    }
	  if (!pararg)
	      pararg = ATH_NULL_STR;
	  str_out = apr_pstrcat(pool, str_out, 
				"\n  arg to parent: ", pararg, 
				"\n  args: ", NULL);
	  while (ath_list_hasnext(dcur->args))
	    {
		arg = 
		  (char *) ath_list_getnext(dcur->args);
		if (!arg)
		    arg = ATH_NULL_STR;
		str_out = apr_pstrcat(pool, str_out, arg, " ", NULL);
	    }
	  /* recurse till null back at top */
	  athd_directive_traverse(&dcur, &tstate);
      }
    str_out = apr_pstrcat(pool, str_out, "\n} BOTTOM\n", NULL);
    return str_out;
}

athd_directive *
athd_directive_for_cmd(athd_directive *dtree, char *cmd)
{
    athd_directive *dcur;
    athd_tree_state tstate = ATHD_TREE_VIS;

    /* start at top, 1st iter */
    dcur = dtree;
    athd_directive_traverse(&dcur, &tstate);

    for (; (dcur); athd_directive_traverse(&dcur, &tstate))
	if (!strcmp(dcur->cmd, cmd))
	    return dcur;
    return NULL;
}
